package com.wang.lotterysystem.service.impl;

import cn.hutool.crypto.digest.DigestUtil;
import com.wang.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.wang.lotterysystem.common.exception.ServiceException;
import com.wang.lotterysystem.common.utils.JWTUtil;
import com.wang.lotterysystem.common.utils.RegexUtil;
import com.wang.lotterysystem.controller.enums.UserIdentityEnum;
import com.wang.lotterysystem.controller.param.user.UserLoginParam;
import com.wang.lotterysystem.controller.param.user.UserMessageLoginParam;
import com.wang.lotterysystem.controller.param.user.UserPasswordLoginParam;
import com.wang.lotterysystem.controller.param.user.UserRegisterParam;
import com.wang.lotterysystem.dao.dataobject.Encrypt;
import com.wang.lotterysystem.dao.dataobject.user.UserDO;
import com.wang.lotterysystem.dao.mapper.UserMapper;
import com.wang.lotterysystem.service.UserService;
import com.wang.lotterysystem.service.VerificationCodeService;
import com.wang.lotterysystem.service.dto.user.UserDTO;
import com.wang.lotterysystem.service.dto.user.UserLoginDTO;
import com.wang.lotterysystem.service.dto.user.UserRegisterDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private VerificationCodeService verificationCodeService;
    @Override
    public UserRegisterDTO register(UserRegisterParam userRegisterParam) {
        //校验参数
        checkRegisterParam(userRegisterParam);
        //创建userDo对象并对其赋值
        UserDO userDO = new UserDO();
        userDO.setUserName(userRegisterParam.getName());
        userDO.setEmail(userRegisterParam.getMail());

        if (StringUtils.hasText(userRegisterParam.getPassword())){
            //如果密码不为空,对密码以sha256方式进行加密
            userDO.setPassword(DigestUtil.sha256Hex(userRegisterParam.getPassword()));
        }
        //把手机号包装为Encrypt类型,以便MyBatis自动解密和加密(对称加密)
        userDO.setPhoneNumber(new Encrypt(userRegisterParam.getPhoneNumber()));
        userDO.setIdentity(userRegisterParam.getIdentity());

        //插入数据库
        userMapper.insert(userDO);

        //构造返回
        UserRegisterDTO userRegisterDTO = new UserRegisterDTO();
        userRegisterDTO.setUserId(userDO.getId());
        return userRegisterDTO;
    }

    @Override
    public UserLoginDTO userLogin(UserLoginParam userLoginParam) {

        UserLoginDTO userLoginDTO = null;

        //判断之后直接进行转换(jdk14之后支持)
        if (userLoginParam instanceof UserPasswordLoginParam passwordLoginParam){
            //如果参数是密码登录参数的实例 --> 调用passwordLogin并返回
            return passwordLogin(passwordLoginParam);
        }else if(userLoginParam instanceof UserMessageLoginParam messageLoginParam){
            //如果参数是验证码登录参数的实例 --> 调用messageLogin并返回
            return messageLogin(messageLoginParam);
        }else{
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_INFO_NOT_EXIST);
        }
    }

    @Override
    public List<UserDTO> findUserList(UserIdentityEnum identity) {
        String identityString = null;

        if (identity == null){
            identityString = null;
        } else {
            identityString = identity.name();
        }

        //根据身份从数据库中查出用户列表, 并存入List<UserDO>中
        List<UserDO> userDOS = userMapper.selectByIdentity(identityString);

        //将List<UserDO>转换为List<UserDTO>并返回
        return convertToUserDTO(userDOS);
    }

    /**
     * 转换返回结果
     * @param userDOS mapper层返回的结果
     * @return 返回Service层指定的返回结果
     */
    private List<UserDTO> convertToUserDTO(List<UserDO> userDOS) {
        List<UserDTO> userDTOS = new ArrayList<>();

        for (UserDO userDO : userDOS) {
            UserDTO userDTO = new UserDTO();
            userDTO.setUserId(userDO.getId());
            userDTO.setIdentity(userDO.getIdentity());
            userDTO.setUserName(userDO.getUserName());
            userDTOS.add(userDTO);
        }
        return userDTOS;
    }


    /**
     * 使用验证码登录
     * @param param 手机号和验证码
     * @return 返回校验是否成功
     */
    private UserLoginDTO messageLogin(UserMessageLoginParam param) {
        String userName = param.getLoginMobile();
        String verification = param.getVerificationCode();

        UserDO userDO = new UserDO();

        //校验手机号格式是否正确
        if (!RegexUtil.checkMobile(userName)){
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
        }

        //查询用户是否存在
        //根据手机号从库中查出用户数据
        userDO = userMapper.selectByPhoneNumber(new Encrypt(userName));

        if (userDO == null){
            //如果结果为空,抛出异常
            throw new ServiceException(ServiceErrorCodeConstants.USER_NOT_EXIST);
        }else if (StringUtils.hasText(param.getMandatoryIdentity()) && !userDO.getIdentity().equals(param.getMandatoryIdentity())){
            //如果存在强制某身份登录 但是 与从数据库数据库中查到的的身份不一致 --> 抛出身份错误
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }

        //校验验证码是否正确
        String code = verificationCodeService.getVerificationCode(userName);
        if (!StringUtils.hasText(code)|| !code.equals(verification)){
            //如果验证码是空的或者验证码不正确
            throw new ServiceException(ServiceErrorCodeConstants.VERIFICATION_CODE_ERROR);
        }

        //构造返回结果
        UserLoginDTO userLoginDTO = new UserLoginDTO();
        //构造 JWTToken
        //1.构造JWT令牌的载荷
        Map<String,Object> map = new HashMap<>();
        map.put("userId",userDO.getId());
        map.put("identity",userDO.getIdentity());
        //2.调用genJwt方法构造JWT令牌
        String token = JWTUtil.genJwt(map);

        userLoginDTO.setToken(token); //设置token
        String identity = UserIdentityEnum.checkForName(userDO.getIdentity()).name(); //校验身份
        userLoginDTO.setIdentity(identity); //设置身份

        return userLoginDTO;
    }

    /**
     * 使用密码登录
     * @param param 参数
     * @return 返回DTO
     */
    private UserLoginDTO passwordLogin(UserPasswordLoginParam param) {
        String userName = param.getLoginName();
        String password = param.getPassword();
        UserDO userDO = null;

        //校验密码不能为空
        if (!StringUtils.hasText(password)){
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }

        //看是通过手机号还是邮箱登录
        if (RegexUtil.checkMail(userName)){
            //如果邮箱格式校验正确 --> 说明是通过邮箱登录
            //根据邮箱从库中查出用户数据
            userDO = userMapper.selectByEmail(userName);
        }else if (RegexUtil.checkMobile(userName)){
            //如果手机号格式校验正确 --> 说明是通过手机号登录
            //根据手机号从库中查出用户数据
            userDO = userMapper.selectByPhoneNumber(new Encrypt(userName)); //注意在使用手机号校验的时候,需要加密之后校验
        }else{
            //如果手机号和密码格式都校验不正确 --> 说明邮箱或手机号格式错误
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_OR_PHONE_ERROR);
        }


        if (userDO == null){
            //校验用户是否存在 (若果从数据库中查出的用户数据为空-->说明用户不存在)
            //如果不存在,抛出异常
            throw new ServiceException(ServiceErrorCodeConstants.USER_NOT_EXIST);
        } else if (StringUtils.hasText(param.getMandatoryIdentity()) && !userDO.getIdentity().equalsIgnoreCase(param.getMandatoryIdentity())){
            //如果存在强制某身份登录 但是 与从数据库数据库中查到的的身份不一致 --> 抛出身份错误
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }else if (!userDO.getPassword().equals(DigestUtil.sha256Hex(password))){
            //如果密码不正确 --> 抛出密码错误
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }

        //构造 JWTToken
        //1.构造JWT令牌的载荷
        Map<String,Object> map = new HashMap<>();
        map.put("userId",userDO.getId());
        map.put("identity",userDO.getIdentity());
        //2.调用genJwt方法构造JWT令牌
        String token = JWTUtil.genJwt(map);

        //构造返回
        UserLoginDTO userLoginDTO = new UserLoginDTO();

        userLoginDTO.setToken(token); //设置token
        String identity = UserIdentityEnum.checkForName(userDO.getIdentity()).name(); //校验身份
        userLoginDTO.setIdentity(identity); //设置身份

        return userLoginDTO;
    }

    /**
     * 对注册参数进行校验
     * @param request 接受传递的参数
     */
    private void checkRegisterParam(UserRegisterParam request){
        //校验参数是否为空
        if (request == null){
            throw new ServiceException(ServiceErrorCodeConstants.REGISTER_INFO_IS_EMPTY);
        }
        //校验手机号与邮箱的格式
        if (!RegexUtil.checkMail(request.getMail())){
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_ERROR);
        }
        if (!RegexUtil.checkMobile(request.getPhoneNumber())){
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
        }
        //校验身份信息
        if (UserIdentityEnum.checkForName(request.getIdentity()) == null){
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }
        //管理员必须设置密码
        if (UserIdentityEnum.ADMIN.name().equals(request.getIdentity())
                && !StringUtils.hasText(request.getPassword())){
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }
        //校验密码格式
        if (StringUtils.hasText(request.getPassword())
                && !RegexUtil.checkPassword(request.getPassword())){
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_FORMAT_ERROR);
        }
        //检查邮箱是否被使用
        if (checkMailUsed(request.getMail())){
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_USED);
        }
        //检查手机号是否被使用
        if (checkPhoneNumberUsed(request.getPhoneNumber())){
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_USED);
        }
    }

    /**
     * 检查邮箱是否被使用
     * @param mail 邮箱
     * @return 返回是否被使用
     */
    private boolean checkMailUsed(String mail){
        if (!StringUtils.hasText(mail)){
            //如果邮箱为空,抛出异常
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_IS_EMPTY);
        }

        //如果根据邮箱从库中查出的数据行数>0 --> 说明邮箱已经被使用
        return userMapper.countByMail(mail) > 0;
    }

    /**
     * 检查手机号是否被使用
     * @param phoneNumber 手机号
     * @return 返回是否被使用
     */
    private boolean checkPhoneNumberUsed(String phoneNumber){
        if (!StringUtils.hasText(phoneNumber)){
            //如果手机号为空,抛出异常
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_IS_EMPTY);
        }
        //构造一个手机号的Encrypt
        Encrypt phone = new Encrypt();
        phone.setValue(phoneNumber);

        //根据手机号的Encrypt从库中查数据: 如果查出的数据行数>0 --> 说明手机号已经被使用
        return userMapper.countByPhoneNumber(phone) > 0;
    }
}